package com.google.mvcframework.servlet;

import com.google.mvcframework.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class KCDispatcherServlet extends HttpServlet {


    private Properties p=new Properties();
    private List<String> classNames=new ArrayList<String>();
    private Map<String,Object>ioc=new HashMap<String, Object>();
    private List<Handler>handlerMapping=new ArrayList<Handler>();



    @Override
    public void init(ServletConfig config) throws ServletException {
        //1.加载配置文件
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
        //2.扫描所有的相关的类
        doScanner(p.getProperty("scanPackage"));
        //3.实例化所有相关的类，并且将其保存到IOC容器中
        doInstance();
        //4.依赖注入
        doAutowired();
        //5.初始化handleMapping
        initHanderMapping();
        System.out.println("KC MVC framework is init");

    }
    //6.等待用户请求
    @Override
    protected void doGet(HttpServletRequest req , HttpServletResponse resp) throws ServletException, IOException {
               doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req , HttpServletResponse resp) throws ServletException, IOException {
//        String url=req.getRequestURI();
//        String contextPath=req.getContextPath();
//        url=url.replace(contextPath,"").replaceAll("/+","/");
//        if(!handlerMapping.containsKey(url)){
//            resp.getWriter().write("404 Not Found!");
//            return;
//        }
//        Method m=handlerMapping.get(url);
//
//
//        Object o =m.invoke(obj,args);
//        System.out.println("========="+m);

        try {
            doDispatch(req,resp);
        }catch (Exception e){
            //如果匹配出现异常，将异常信息打印出去
            resp.getWriter().write("500 Exception,Details:\r\n"+Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]","").replaceAll(",\\s","\r\n"));
        }
    }
    private void doLoadConfig(String location){
        InputStream fis=null;
        try {
            fis=this.getClass().getClassLoader().getResourceAsStream(location);
            //1.读取配置文件
            p.load(fis);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if(null!=fis){fis.close();}
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
    //递归扫描出所有的Class文件
    private void doScanner(String packageName){
        //将所有的包路径转换为文件路径
        URL url=this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.","/"));
        File dir=new File(url.getFile());
        for(File file:dir.listFiles()){
            //如果是文件夹，继续递归
            if(file.isDirectory()){
                doScanner(packageName+"."+file.getName());
            }
            else {
                classNames.add(packageName+"."+file.getName().replace(".class","").trim());
            }
        }
    }
    private void doInstance(){
        if(classNames.isEmpty()){return;}
        try {
            for(String className:classNames){
                Class<?> clazz=Class.forName(className);
                //不是所有的类都要初始化 加了controller和service注解的才要初始化
                if(clazz.isAnnotationPresent(KCController.class)){
                    //默认将首字母小写作为beanName
                    String beanName=lowerFirst(clazz.getSimpleName());
                    ioc.put(beanName,clazz.newInstance());
                }else if(clazz.isAnnotationPresent(KCService.class)){

                    Object obj=clazz.newInstance();
                    KCService service=clazz.getAnnotation(KCService.class);
                    String beanName=service.value();

                    //如果用户设置了名字，就用用户自己设置
                    if(!"".equals(beanName.trim())){
                        ioc.put(beanName,obj);
                        continue;
                    }

                    //如果自己没有设置，就按接口类型创建一个实例
                    Class<?>[]interfaces=clazz.getInterfaces();
                    for(Class<?> i:interfaces){
                        ioc.put(i.getName(),obj);
                    }

                }
                else {
                    continue;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    private void doAutowired(){
          if(ioc.isEmpty()){return;}
          for(Map.Entry<String,Object> entry:ioc.entrySet()){
              //拿到实例对象中所有属性
              Field[]fields=entry.getValue().getClass().getDeclaredFields();
              for(Field field:fields){
                  //没有KCAutowired这个注解的属性，不给它注入
                  if(!field.isAnnotationPresent(KCAutowired.class)){continue;}

                  KCAutowired autowired=field.getAnnotation(KCAutowired.class);
                  String beanName=autowired.value().trim();
                  if("".equals(beanName)){
                      beanName=field.getType().getName();
                  }
                  field.setAccessible(true);//设置私有属性访问权限
                  try {
                      field.set(entry.getValue(),ioc.get(beanName));
                  }catch (Exception e){
                      e.printStackTrace();
                      continue;
                  }
              }
          }
    }
    private void initHanderMapping(){
        //将url和Method进行一对一关联
        if(ioc.isEmpty()){return;}

        for(Map.Entry<String,Object> entry :ioc.entrySet()){
            Class<?>clazz =entry.getValue().getClass();
            //判断是否有controller注解
            if(!clazz.isAnnotationPresent(KCController.class)){continue;}

            String baseUrl="";
            if(clazz.isAnnotationPresent(KCRequestMapping.class)){
                KCRequestMapping requestMapping=clazz.getAnnotation(KCRequestMapping.class);
                baseUrl=requestMapping.value();
            }
            Method[] methods=clazz.getMethods();
            for(Method method:methods){
                if(!method.isAnnotationPresent(KCRequestMapping.class)){continue;}
                KCRequestMapping requestMapping=method.getAnnotation(KCRequestMapping.class);
                String methodUrl=requestMapping.value();
                String url=(baseUrl+methodUrl).replaceAll("/+","/");
                Pattern pattern=Pattern.compile(url);
                handlerMapping.add(new Handler(pattern,entry.getValue(),method));
                System.out.println("mapping"+url+","+method);
                //System.out.println("Mapping:"+url+",Method:"+method);

            }
        }
    }

    //首字母小写
    private String lowerFirst(String str){
        char []chars =str.toCharArray();
        chars[0] +=32;
        return String.valueOf(chars);
    }


    private void doDispatch(HttpServletRequest req,HttpServletResponse resp)throws Exception{
        try {
            Handler handler=getHandler(req);
            if(handler==null){
                //如果没有匹配上 返回404错误
                resp.getWriter().write("404 Not Found");
                return;
            }

            //获取方法的参数列表
            Class<?>[] paramTypes=handler.method.getParameterTypes();
            //保存所有需要自动赋值的参数值
            Object []paramValues=new Object[paramTypes.length];
            Map<String ,String []>params=req.getParameterMap();
            for(Map.Entry<String,String[]> param:params.entrySet()){
                String value=Arrays.toString(param.getValue()).replaceAll("\\[|\\]","").replaceAll(",\\s",",");

                //如果找到匹配的对象，则开始填充参数值
                if(!handler.paramIndexMapping.containsKey(param.getKey())){continue;}
                int index=handler.paramIndexMapping.get(param.getKey());
                paramValues[index]=convert(paramTypes[index],value);
            }

            //设置方法中的request和response对象
            int reqIndex=handler.paramIndexMapping.get(HttpServletRequest.class.getName());
            paramValues[reqIndex]=req;
            int respIndex=handler.paramIndexMapping.get(HttpServletResponse.class.getName());
            paramValues[respIndex]=resp;

            handler.method.invoke(handler.controller,paramValues);
        }catch (Exception e){
            throw e;
        }
    }

    private Handler getHandler(HttpServletRequest req) throws Exception{
        if(handlerMapping.isEmpty()){return null;}
        String url =req.getRequestURI();
        String contextPath=req.getContextPath();
        url=url.replace(contextPath,"").replaceAll("/+","");
        for(Handler handler:handlerMapping){
            try{
                //正则匹配
                Matcher matcher=handler.pattern.matcher(url);
                //如果没有匹配上 继续下一个匹配
                if(!matcher.matches()){continue;}
                return handler;
            }catch (Exception e){
                throw e;
            }
        }
        return null;
    }

    private Object convert(Class<?> type,String value){
        if(Integer.class==type){
            return Integer.valueOf(value);
        }
        return value;
    }



    private class Handler{
        protected Object controller;//保存方法对应的实例
        protected Method method;//保存映射的方法
        protected Pattern pattern;
        protected Map<String,Integer> paramIndexMapping;//参数顺序
        /*构造一个Handler基本参数
        @param controller
        @param method
        @param paramMapping
         */
        protected  Handler(Pattern pattern,Object controller,Method method){
            this.controller=controller;
            this.method=method;
            this.pattern=pattern;
            paramIndexMapping =new HashMap<String,Integer>();
            putParamIndexMappping(method);
        }

        private  void putParamIndexMappping(Method method){

        }
    }

}
